"""
pytest配置和共享fixture

提供测试所需的共享配置、fixture和工具函数。
"""

import pytest
import tempfile
import shutil
import asyncio
from pathlib import Path
from unittest.mock import Mock, MagicMock
from typing import Dict, Any, Generator

import sys
import os

# 添加项目根目录到Python路径
project_root = Path(__file__).parent.parent
sys.path.insert(0, str(project_root))

try:
    from src.bootstrap import bootstrap_application, shutdown_application, get_service
    from src.core.di import Injector
    # from src.database.connection import DatabaseManager
    # from src.models import Video, Project, VideoClassification
except ImportError:
    # 如果导入失败，创建Mock对象
    bootstrap_application = lambda: None
    shutdown_application = lambda: None
    get_service = lambda x: None
    Injector = None


# ============================================================================
# 测试配置
# ============================================================================

def pytest_configure(config):
    """pytest配置"""
    # 设置测试标记
    config.addinivalue_line("markers", "unit: 单元测试")
    config.addinivalue_line("markers", "integration: 集成测试")
    config.addinivalue_line("markers", "slow: 慢速测试")
    config.addinivalue_line("markers", "async_test: 异步测试")
    config.addinivalue_line("markers", "database: 需要数据库的测试")
    config.addinivalue_line("markers", "external: 需要外部依赖的测试")


def pytest_collection_modifyitems(config, items):
    """修改测试收集"""
    # 为异步测试添加标记
    for item in items:
        if asyncio.iscoroutinefunction(item.function):
            item.add_marker(pytest.mark.async_test)


# ============================================================================
# 基础Fixtures
# ============================================================================

@pytest.fixture(scope="session")
def event_loop():
    """创建事件循环"""
    loop = asyncio.new_event_loop()
    yield loop
    loop.close()


@pytest.fixture(scope="session")
def temp_dir():
    """创建临时目录"""
    temp_path = tempfile.mkdtemp(prefix="mixvideo_test_")
    yield Path(temp_path)
    shutil.rmtree(temp_path, ignore_errors=True)


@pytest.fixture
def test_config():
    """测试配置"""
    return {
        'database': {
            'url': 'sqlite:///:memory:',
            'echo': False
        },
        'ai': {
            'gemini': {
                'api_key': 'test_api_key',
                'model': 'gemini-pro'
            }
        },
        'video': {
            'ffmpeg_path': '/usr/bin/ffmpeg',
            'opencv_enabled': True,
            'supported_formats': ['mp4', 'avi', 'mov']
        },
        'logging': {
            'level': 'DEBUG',
            'file': None  # 不写入文件
        }
    }


# ============================================================================
# 应用程序Fixtures
# ============================================================================

@pytest.fixture(scope="function")
def app_context(test_config, temp_dir):
    """应用程序上下文"""
    # 设置测试环境变量
    os.environ['MIXVIDEO_TEST_MODE'] = 'true'
    os.environ['MIXVIDEO_DATABASE_URL'] = test_config['database']['url']
    os.environ['MIXVIDEO_DATA_DIR'] = str(temp_dir)
    
    try:
        # 初始化应用程序
        bootstrap_application()
        yield
    finally:
        # 清理
        try:
            shutdown_application()
        except:
            pass
        
        # 清理环境变量
        os.environ.pop('MIXVIDEO_TEST_MODE', None)
        os.environ.pop('MIXVIDEO_DATABASE_URL', None)
        os.environ.pop('MIXVIDEO_DATA_DIR', None)


@pytest.fixture
def dependency_injector():
    """依赖注入器"""
    Injector.reset()
    injector = Injector()
    yield injector
    injector.clear()


# ============================================================================
# 数据库Fixtures
# ============================================================================

@pytest.fixture
def db_manager(test_config):
    """数据库管理器"""
    manager = DatabaseManager(test_config['database']['url'])
    manager.initialize()
    yield manager
    manager.close()


@pytest.fixture
def db_session(db_manager):
    """数据库会话"""
    session = db_manager.get_session()
    try:
        yield session
    finally:
        session.rollback()
        session.close()


# ============================================================================
# Mock Fixtures
# ============================================================================

@pytest.fixture
def mock_ffmpeg():
    """Mock FFmpeg"""
    mock = Mock()
    mock.probe.return_value = {
        'streams': [
            {
                'codec_type': 'video',
                'width': 1920,
                'height': 1080,
                'duration': '10.0',
                'avg_frame_rate': '30/1'
            }
        ],
        'format': {
            'duration': '10.0',
            'size': '1048576',
            'format_name': 'mp4'
        }
    }
    mock.run.return_value = (b'', b'')
    return mock


@pytest.fixture
def mock_opencv():
    """Mock OpenCV"""
    mock = Mock()
    mock.VideoCapture.return_value.get.side_effect = lambda prop: {
        3: 1920,  # CV_CAP_PROP_FRAME_WIDTH
        4: 1080,  # CV_CAP_PROP_FRAME_HEIGHT
        5: 30.0,  # CV_CAP_PROP_FPS
        7: 300    # CV_CAP_PROP_FRAME_COUNT
    }.get(prop, 0)
    mock.VideoCapture.return_value.read.return_value = (True, Mock())
    return mock


@pytest.fixture
def mock_gemini_api():
    """Mock Gemini API"""
    mock = Mock()
    mock.generate_content.return_value.text = '{"category": "product_showcase", "confidence": 0.85, "reasoning": "测试分类"}'
    return mock


# ============================================================================
# 测试数据Fixtures
# ============================================================================

@pytest.fixture
def sample_video_data():
    """示例视频数据"""
    return {
        'file_path': '/test/sample_video.mp4',
        'file_name': 'sample_video.mp4',
        'file_size': 1048576,
        'duration': 10.5,
        'width': 1920,
        'height': 1080,
        'fps': 30.0,
        'format': 'mp4',
        'codec': 'h264'
    }


@pytest.fixture
def sample_project_data():
    """示例项目数据"""
    return {
        'name': '测试项目',
        'description': '这是一个测试项目',
        'output_format': 'mp4',
        'settings': {
            'canvas': {
                'width': 1920,
                'height': 1080,
                'fps': 30.0
            }
        }
    }


@pytest.fixture
def sample_classification_data():
    """示例分类数据"""
    return {
        'category': 'product_showcase',
        'confidence': 0.85,
        'classifier_type': 'gemini',
        'reasoning': '视频展示了产品的特性和使用方法',
        'processing_time': 1.5
    }


# ============================================================================
# 工作流测试Fixtures
# ============================================================================

@pytest.fixture
def sample_workflow_data():
    """示例工作流数据"""
    return {
        'name': '测试工作流',
        'description': '用于测试的工作流',
        'version': '1.0.0',
        'nodes': {
            'input': {
                'type': 'input',
                'name': '输入节点',
                'data': {'video_path': '/test/input.mp4'}
            },
            'process': {
                'type': 'processing',
                'name': '处理节点',
                'processor': 'test_processor'
            },
            'output': {
                'type': 'output',
                'name': '输出节点',
                'output_key': 'result'
            }
        },
        'connections': [
            {'from': 'input', 'to': 'process'},
            {'from': 'process', 'to': 'output'}
        ]
    }


# ============================================================================
# 文件系统Fixtures
# ============================================================================

@pytest.fixture
def test_video_file(temp_dir):
    """创建测试视频文件"""
    video_file = temp_dir / "test_video.mp4"
    # 创建一个空文件模拟视频
    video_file.write_bytes(b'\x00' * 1024)  # 1KB的空文件
    return video_file


@pytest.fixture
def test_video_files(temp_dir):
    """创建多个测试视频文件"""
    files = []
    for i in range(3):
        video_file = temp_dir / f"test_video_{i}.mp4"
        video_file.write_bytes(b'\x00' * (1024 * (i + 1)))  # 不同大小
        files.append(video_file)
    return files


@pytest.fixture
def test_config_file(temp_dir, test_config):
    """创建测试配置文件"""
    import yaml
    config_file = temp_dir / "test_config.yaml"
    with open(config_file, 'w', encoding='utf-8') as f:
        yaml.dump(test_config, f, default_flow_style=False, allow_unicode=True)
    return config_file


# ============================================================================
# 性能测试Fixtures
# ============================================================================

@pytest.fixture
def performance_timer():
    """性能计时器"""
    import time
    
    class Timer:
        def __init__(self):
            self.start_time = None
            self.end_time = None
        
        def start(self):
            self.start_time = time.time()
        
        def stop(self):
            self.end_time = time.time()
        
        @property
        def elapsed(self):
            if self.start_time and self.end_time:
                return self.end_time - self.start_time
            return None
    
    return Timer()


# ============================================================================
# 辅助函数
# ============================================================================

def create_test_video_record(session, **kwargs):
    """创建测试视频记录"""
    default_data = {
        'file_path': '/test/video.mp4',
        'file_name': 'video.mp4',
        'file_size': 1048576,
        'duration': 10.0,
        'width': 1920,
        'height': 1080,
        'fps': 30.0,
        'format': 'mp4'
    }
    default_data.update(kwargs)
    
    video = Video(**default_data)
    session.add(video)
    session.commit()
    return video


def create_test_project_record(session, **kwargs):
    """创建测试项目记录"""
    default_data = {
        'name': '测试项目',
        'description': '测试项目描述',
        'output_format': 'mp4'
    }
    default_data.update(kwargs)
    
    project = Project(**default_data)
    session.add(project)
    session.commit()
    return project


def assert_video_data_equal(actual, expected, ignore_fields=None):
    """断言视频数据相等"""
    ignore_fields = ignore_fields or ['id', 'created_at', 'updated_at']
    
    for field in expected:
        if field not in ignore_fields:
            assert getattr(actual, field) == expected[field], f"字段 {field} 不匹配"


def assert_project_data_equal(actual, expected, ignore_fields=None):
    """断言项目数据相等"""
    ignore_fields = ignore_fields or ['id', 'created_at', 'updated_at']
    
    for field in expected:
        if field not in ignore_fields:
            assert getattr(actual, field) == expected[field], f"字段 {field} 不匹配"
